package com.atguigu.gmall.gateway.filter;

import com.atguigu.gmall.common.constans.RedisConstant;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import com.atguigu.gmall.common.util.JsonUtil;
import com.atguigu.gmall.gateway.config.AppUrlProperties;
import com.atguigu.gmall.model.user.UserInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @author A
 * @version 1.0
 * @description: TODO
 * @date 2025/9/9 0:47
 */
@Component
public class UserAuthFilter implements GlobalFilter {
    @Autowired
    AppUrlProperties appUrlProperties;
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    StringRedisTemplate redisTemplate;

    //响应式编程
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //拦截请求
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath();
        //System.out.println("path: " + path);
        //拦截静态资源请求,放行
        for (String staticUrl : appUrlProperties.getEveryone()) {
            boolean match = antPathMatcher.match(staticUrl, path);
            if (match) {
                return chain.filter(exchange);
            }
        }
        //拦截非静态资源请求,判断是否是内部请求
        for (String forbiddenUrl : appUrlProperties.getForbidden()) {
            boolean match = antPathMatcher.match(forbiddenUrl, path);
            if (match) {
                //是内部请求,返回错误信息
                Result<String> result = Result.build("内部请求,禁止访问!", ResultCodeEnum.FORBIDDEN);
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                DataBuffer wrap = response.bufferFactory().wrap(JsonUtil.toStr(result).getBytes(StandardCharsets.UTF_8));
                Mono<DataBuffer> just = Mono.just(wrap);

                return response.writeWith(just);
            }
        }
        //拦截必须认证的请求,判断是否登录
        for (String mustAuthUrl : appUrlProperties.getMustAuth()) {
            boolean match = antPathMatcher.match(mustAuthUrl, path);
            if (match) {
                //该请求必须登录(验证token),判断是否登录
                Boolean isLogin = validateToken(request);
                if (!isLogin){
                    return redirectToLogin(response, request);
                }
                //登录成功,放行
                //放行前透传用户id
            }
        }
        //不用拦截的请求,放行
        //1.验证是否有token
        String token = tokenIsExists(request);
        if (StringUtils.isEmpty(token)){
            //token不存在,直接放行
            //判断是否有临时用户id
            String userTempId = UserTempIdIsExists(request);
            ServerHttpRequest newRequest = request.mutate()
                    .header("UserTempId",userTempId ).build();
            ServerWebExchange newWebExchange = exchange.mutate()
                    .request(newRequest)
                    .response(response)
                    .build();

            return chain.filter(newWebExchange);
        }
        //2.验证token是否有效
        Boolean isTokenValid = validateToken(request);
        if (!isTokenValid){
            //token无效,重定向到登录页面
            return redirectToLogin(response, request);
        }

        //3.token有效,需要透传用户id,放行
        UserInfo userInfo =getUserInfoByTrueToken(request,token);
        //request.getHeaders().add("userId", String.valueOf(userInfo.getId()));
        String userTempId = UserTempIdIsExists(request);
        //请求头只读,不允许修改
        ServerHttpRequest newRequest = request.mutate()
                .header("UserId", String.valueOf(userInfo.getId()))
                .header("UserTempId",userTempId )
                .build();
        ServerWebExchange newWebExchange = exchange.mutate()
                .request(newRequest)
                .response(response)
                .build();
        Mono<Void> mono = chain.filter(newWebExchange);

        return mono;
    }
    //根据正确有效的token获取用户信息
    private UserInfo getUserInfoByTrueToken(ServerHttpRequest request, String token) {
        String userInfoStr = redisTemplate.opsForValue().get(RedisConstant.USER_TOKEN_PREFIX + token);
        return JsonUtil.jsonToObject(userInfoStr, new TypeReference<UserInfo>() {
        });
    }

    //重定向到登录页面
    private Mono<Void> redirectToLogin(ServerHttpResponse response, ServerHttpRequest request) {
        //未登录,重定向到登录页面
        response.setStatusCode(HttpStatus.FOUND);
        //http://passport.gmall.com/login.html?originUrl=http://gmall.com/
        String originUrl = request.getURI().toString();
        response.getHeaders().set("Location",appUrlProperties.getLoginAddress()+"?originUrl="+originUrl);

        //设置cookie,防止无限重定向  maxAge=0L 立即失效  -1L 代表浏览器关闭失效
        ResponseCookie responseCookie = ResponseCookie.from("token", "123124124")
                .maxAge(0L)
                .domain(".gmall.com")
                .build();
        response.addCookie(responseCookie);
        return response.setComplete();
    }

    private String tokenIsExists(ServerHttpRequest request) {
        //1.先从cookie中获取token
        HttpCookie cookie = request.getCookies().getFirst("token");
        String token = null;
        if (cookie != null) {
            token = cookie.getValue();
        } else {
            //从header中获取token
            String tokenHeader = request.getHeaders().getFirst("token");
            token = tokenHeader;
        }
        return token;
    }
    private String UserTempIdIsExists(ServerHttpRequest request) {
        //1.先从cookie中获取token
        HttpCookie cookie = request.getCookies().getFirst("userTempId");
        String userTempId = null;
        if (cookie != null) {
            userTempId = cookie.getValue();
        } else {
            //从header中获取token
            String userTempIdHeader = request.getHeaders().getFirst("userTempId");
            userTempId = userTempIdHeader;
        }
        return userTempId;
    }

    private Boolean validateToken(ServerHttpRequest request) {
        String token = tokenIsExists(request);
        if (StringUtils.isEmpty(token)){
            //token不存在,返回错误信息
            return false;
        }
        //2.验证token是否有效
        String userInfoStr = redisTemplate.opsForValue().get(RedisConstant.USER_TOKEN_PREFIX + token);
        if (userInfoStr == null) {
            //token无效,返回错误信息
            return false;
        }
        //3.token有效,验证用户信息的ip是否一致
        UserInfo userInfo = checkUserIp(request, userInfoStr);
        //ip不一致,返回错误信息
        //ip一致,返回正确信息
        return userInfo != null;
    }

    //验证用户信息的ip是否一致,返回正确信息
    private UserInfo checkUserIp(ServerHttpRequest request, String userInfoStr) {
        String ipAddressNow = IpUtil.getGatwayIpAddress(request);
        UserInfo userInfo = JsonUtil.jsonToObject(userInfoStr, new TypeReference<UserInfo>() {
        });
        if (ipAddressNow.equals(userInfo.getIpAddress())) {
            return userInfo;
        }
        //ip不一致,返回错误信息
        return null;
    }
}
